package com.myk.game.lib.utils;

import java.io.Serializable;

/**
 * 调用者。可以通过此类的方法获取调用者、多级调用者以及判断是否被调用
 */
public class CallerUtil {

	private static final Caller INSTANCE;
	static {
		INSTANCE = tryCreateCaller();
	}

	/**
	 * 获得调用者
	 * 
	 * @return 调用者
	 */
	public static Class<?> getCaller() {
		return INSTANCE.getCaller();
	}

	/**
	 * 获得调用者的调用者
	 * 
	 * @return 调用者的调用者
	 */
	public static Class<?> getCallerCaller() {
		return INSTANCE.getCallerCaller();
	}

	/**
	 * 获得调用者，指定第几级调用者<br>
	 * 调用者层级关系：
	 * 
	 * <pre>
	 * 0 {@link CallerUtil}
	 * 1 调用{@link CallerUtil}中方法的类
	 * 2 调用者的调用者
	 * ...
	 * </pre>
	 * 
	 * @param depth 层级。0表示{@link CallerUtil}本身，1表示调用{@link CallerUtil}的类，2表示调用者的调用者，依次类推
	 * @return 第几级调用者
	 */
	public static Class<?> getCaller(int depth) {
		return INSTANCE.getCaller(depth);
	}

	/**
	 * 是否被指定类调用
	 * 
	 * @param clazz 调用者类
	 * @return 是否被调用
	 */
	public static boolean isCalledBy(Class<?> clazz) {
		return INSTANCE.isCalledBy(clazz);
	}

	/**
	 * 获取调用此方法的方法名
	 *
	 * @param isFullName 是否返回全名，全名包括方法所在类的全路径名
	 * @return 调用此方法的方法名
	 */
	public static String getCallerMethodName(boolean isFullName){
		final StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2];
		final String methodName = stackTraceElement.getMethodName();
		if(!isFullName){
			return methodName;
		}

		return stackTraceElement.getClassName() + "." + methodName;
	}

	/**
	 * 尝试创建{@link Caller}实现
	 * 
	 * @return {@link Caller}实现
	 */
	private static Caller tryCreateCaller() {
		Caller caller;
		try {
			caller = new SecurityManagerCaller();
			if(null != caller.getCaller() && null != caller.getCallerCaller()) {
				return caller;
			}
		} catch (Throwable e) {
			//ignore
		}
		
		caller = new StackTraceCaller();
		return caller;
	}

	/**
	 * 调用者接口<br>
	 * 可以通过此接口的实现类方法获取调用者、多级调用者以及判断是否被调用
	 */
	public interface Caller {
		/**
		 * 获得调用者
		 *
		 * @return 调用者
		 */
		Class<?> getCaller();

		/**
		 * 获得调用者的调用者
		 *
		 * @return 调用者的调用者
		 */
		Class<?> getCallerCaller();

		/**
		 * 获得调用者，指定第几级调用者 调用者层级关系：
		 *
		 * <pre>
		 * 0 {@link CallerUtil}
		 * 1 调用{@link CallerUtil}中方法的类
		 * 2 调用者的调用者
		 * ...
		 * </pre>
		 *
		 * @param depth 层级。0表示{@link CallerUtil}本身，1表示调用{@link CallerUtil}的类，2表示调用者的调用者，依次类推
		 * @return 第几级调用者
		 */
		Class<?> getCaller(int depth);

		/**
		 * 是否被指定类调用
		 *
		 * @param clazz 调用者类
		 * @return 是否被调用
		 */
		boolean isCalledBy(Class<?> clazz);
	}

	/**
	 * {@link SecurityManager} 方式获取调用者
	 */
	public static class SecurityManagerCaller extends SecurityManager implements Caller, Serializable {

		private static final long serialVersionUID = 1L;
		private static final int OFFSET = 1;

		@Override
		public Class<?> getCaller() {
			final Class<?>[] context = getClassContext();
			if (null != context && (OFFSET + 1) < context.length) {
				return context[OFFSET + 1];
			}
			return null;
		}

		@Override
		public Class<?> getCallerCaller() {
			final Class<?>[] context = getClassContext();
			if (null != context && (OFFSET + 2) < context.length) {
				return context[OFFSET + 2];
			}
			return null;
		}

		@Override
		public Class<?> getCaller(int depth) {
			final Class<?>[] context = getClassContext();
			if (null != context && (OFFSET + depth) < context.length) {
				return context[OFFSET + depth];
			}
			return null;
		}

		@Override
		public boolean isCalledBy(Class<?> clazz) {
			final Class<?>[] classes = getClassContext();
			if (isNotEmpty(classes)) {
				for (Class<?> contextClass : classes) {
					if (contextClass.equals(clazz)) {
						return true;
					}
				}
			}
			return false;
		}

		public static <T> boolean isNotEmpty(T[] array) {
			return (array != null && array.length != 0);
		}
	}

	/**
	 * 通过StackTrace方式获取调用者。此方式效率最低，不推荐使用
	 */
	public static class StackTraceCaller implements Caller, Serializable {

		private static final long serialVersionUID = 1L;
		private static final int OFFSET = 2;

		@Override
		public Class<?> getCaller() {
			final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
			if (OFFSET + 1 >= stackTrace.length) {
				return null;
			}
			final String className = stackTrace[OFFSET + 1].getClassName();
			try {
				return Class.forName(className);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException("[" + className + "] not found!", e);
			}
		}

		@Override
		public Class<?> getCallerCaller() {
			final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
			if (OFFSET + 2 >= stackTrace.length) {
				return null;
			}
			final String className = stackTrace[OFFSET + 2].getClassName();
			try {
				return Class.forName(className);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException("[" + className + "] not found!", e);
			}
		}

		@Override
		public Class<?> getCaller(int depth) {
			final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
			if (OFFSET + depth >= stackTrace.length) {
				return null;
			}
			final String className = stackTrace[OFFSET + depth].getClassName();
			try {
				return Class.forName(className);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException("[" + className + "] not found!", e);
			}
		}

		@Override
		public boolean isCalledBy(Class<?> clazz) {
			final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
			for (final StackTraceElement element : stackTrace) {
				if (element.getClassName().equals(clazz.getName())) {
					return true;
				}
			}
			return false;
		}
	}
}
